/* 
 * Copyright (C) 2013, 2014 lex xiang
 *
 * file:	sys/s3c2440.c
 * common:	lib of cpu handler
 * history:	2013	Aug 11 created by lex xiang
 */

#include "s3c2440.h"
#include "s3c2440slib.h"
#include "types.h"
#include "uart.h"

void exc_undef_handler(void)
{
	printf_d("Undefined instruction exception!!!\n");
	while(1);
}

void exc_swi_handler(void)
{
	printf_d("SWI exception!!!\n");
	while(1);
}

void exc_pabort_handler(void)
{
	printf_d("Pabort exception!!!\n");
	while(1);
}

void exc_dabort_handler(void)
{
	printf_d("Dabort exception!!!\n");
	while(1);
}

void exc_init()
{
	REG32(EXC_UNDEF_ADDR)	= (u32)exc_undef_handler;
	REG32(EXC_SWI_ADDR)	= (u32)exc_swi_handler;
	REG32(EXC_PABORT_ADDR)	= (u32)exc_pabort_handler;
	REG32(EXC_DABORT_ADDR)	= (u32)exc_dabort_handler;

	REG32(INT_MODE_CTRL)	= 0x0;
	REG32(INT_MASK_CTRL)	= 0xffffffff;

	return;
}

void gpio_init(void)
{
	REG32(GPA_CTRL)		= 0x7fffff; 

	REG32(GPB_CTRL)		= 0x015550;
	REG32(GPB_PUP_CTRL)	= 0x7ff;	/* The pull up function is disabled */

	REG32(GPC_CTRL)		= 0xaaa956aa;
	REG32(GPC_PUP_CTRL)	= 0xffff;	/* The pull up function is disabled */

	REG32(GPD_CTRL)		= 0xaaaaaaaa;
	REG32(GPD_PUP_CTRL)	= 0xffff;	/* The pull up function is disabled */

	REG32(GPE_CTRL)		= 0xa02aa800;	/* For added AC97 setting      	    */
	REG32(GPE_PUP_CTRL)	= 0xffff;    

	REG32(GPF_CTRL)		= 0x55aa;
	REG32(GPF_PUP_CTRL)	= 0xff;		/* The pull up function is disabled */

	REG32(GPG_CTRL)		= 0x00a2aaaa;	/* GPG9 input without pull-up	    */
	REG32(GPG_PUP_CTRL)	= 0xffff;	/* The pull up function is disabled */

	REG32(GPH_CTRL)		= 0x00faaa;
	REG32(GPH_PUP_CTRL)	= 0x7ff;	/* The pull up function is disabled */

	REG32(GPJ_CTRL)		= 0x02aaaaaa;
	REG32(GPJ_PUP_CTRL)	= 0x1fff;	/* The pull up function is disabled */

	return;
}

void change_mpll(s32 mdiv, s32 pdiv, s32 sdiv)
{
	REG32(MPLL_CTRL) = (mdiv << 12) | (pdiv << 4) | sdiv;
}

void change_clk_div(int hdivn_val,int pdivn_val)
{
	int hdivn=2, pdivn=0;
	
	switch(hdivn_val) {
		case 11: hdivn=0; break;
		case 12: hdivn=1; break;
		case 13:
		case 16: hdivn=3; break;
		case 14: 
		case 18: hdivn=2; break;
	}
	
	switch(pdivn_val) {
		case 11: pdivn=0; break;
		case 12: pdivn=1; break;
	}
	
	REG32(CLK_DIV_CTRL) = (hdivn << 1) | pdivn;

	switch(hdivn_val) {
	case 16:	/* when 1, HCLK=FCLK/8. */
		REG32(CAM_DIV_CTRL) = (REG32(CAM_DIV_CTRL) & ~(3<<8)) | (1<<8); 
		break; 
	case 18: 	/* when 1, HCLK=FCLK/6. */
		REG32(CAM_DIV_CTRL) = (REG32(CAM_DIV_CTRL) & ~(3<<8)) | (1<<9); 
		break;
	}
	
	if(hdivn!=0)
		MMU_SetAsyncBusMode();
	else 
		MMU_SetFastBusMode();
}

u32 cpu_freq;
u32 FCLK;
u32 HCLK;
u32 PCLK;
u32 UCLK;
u32 UPLL;

void cal_cpu_bus_clk(void)
{
	u32 val;
	u8 m, p, s;
	
	val	= REG32(MPLL_CTRL);
	m	= (val >> 12) & 0xff;
	p	= (val >> 4) & 0x3f;
	s	= val & 3;

	/* (m + 8) * FIN * 2 */
	FCLK = ((m + 8) * (FIN / 100) * 2) / ((p + 2) * (1 << s)) * 100;
	
	val	= REG32(CLK_DIV_CTRL);
	m	= (val >> 1) & 3;
	p	= val & 1;	

	val	= REG32(CAM_DIV_CTRL);
	s	= val >> 8;
	
	switch (m) {
	case 0:
		HCLK = FCLK;
		break;
	case 1:
		HCLK = FCLK >> 1;
		break;
	case 2:
		if(s&2)
			HCLK = FCLK >> 3;
		else
			HCLK = FCLK >> 2;
		break;
	case 3:
		if(s&1)
			HCLK = FCLK / 6;
		else
			HCLK = FCLK / 3;
		break;
	}
	
	if(p)
		PCLK = HCLK >> 1;
	else
		PCLK = HCLK;
	
	if(s & 0x10)
		cpu_freq = HCLK;
	else
		cpu_freq = FCLK;
		
	val	= REG32(UPLL_CTRL);
	m	= (val >> 12) & 0xff;
	p	= (val >> 4) & 0x3f;
	s	= val & 3;
	UPLL = ((m + 8) * FIN) / ((p + 2) * (1 << s));
	UCLK = (REG32(CLK_DIV_CTRL) & 8) ? (UPLL >> 1) : UPLL;

	return;
}


void clk_init()
{
	u32 mpll = 0;
	u8  key, i;

	/* don't use 100M!
	 * boot_params.cpu_clk.val = 3;
	 */
	i = 2;

	switch ( i ) {
	case 0:	/* 200M */
		key  = 12;
		mpll = (92<<12)|(4<<4)|(1);
		break;
	case 1:	/* 300M */
		key  = 13;
		mpll = (67<<12)|(1<<4)|(1);
		break;
	case 2:	/* 400 */
		key  = 14;
		mpll = (92<<12)|(1<<4)|(1);
		break;
	case 3:	/* 440!!! */
		key  = 14;
		mpll = (102<<12)|(1<<4)|(1);
		break;
	default:
		key  = 14;
		mpll = (92<<12)|(1<<4)|(1);
		break;
	}
	
	/* 
	 * init FCLK=400M, so change MPLL first
	 */
	change_mpll((mpll >> 12) & 0xff, (mpll >> 4) & 0x3f, mpll & 3);

	change_clk_div(key, 12);

	cal_cpu_bus_clk();

	return;
}
